<template>
  <div
      class="bin-rate"
      @keydown="handleKey"
      role="slider"
      :aria-valuenow="currentValue"
      :aria-valuetext="text"
      aria-valuemin="0"
      :aria-valuemax="max"
      tabindex="0">
    <span
        v-for="(item, key) in max"
        class="bin-rate__item"
        @mousemove="setCurrentValue(item, $event)"
        @mouseleave="resetCurrentValue"
        @click="selectValue(item)"
        :style="{ cursor: rateDisabled ? 'auto' : 'pointer' }"
        :key="key">
      <i
          :class="[classes[item - 1], { 'hover': hoverIndex === item }]"
          class="bin-rate__icon iconfont"
          :style="getIconStyle(item)">
        <i
            v-if="showDecimalIcon(item)"
            :class="decimalIconClass"
            :style="decimalStyle"
            class="bin-rate__decimal iconfont">
        </i>
      </i>
    </span>
    <span v-if="showText || showScore" class="bin-rate__text" :style="{ color: textColor }">{{ text }}</span>
  </div>
</template>

<script>
import { hasClass } from '../../utils/dom'

export default {
  name: 'BRate',
  data() {
    return {
      pointerAtLeftHalf: true,
      currentValue: this.value,
      hoverIndex: -1
    }
  },

  props: {
    value: {
      type: Number,
      default: 0
    },
    lowThreshold: {
      type: Number,
      default: 2
    },
    highThreshold: {
      type: Number,
      default: 4
    },
    max: {
      type: Number,
      default: 5
    },
    colors: {
      type: Array,
      default() {
        return ['#F7BA2A', '#F7BA2A', '#F7BA2A']
      }
    },
    voidColor: {
      type: String,
      default: '#C6D1DE'
    },
    disabledVoidColor: {
      type: String,
      default: '#EFF2F7'
    },
    iconClasses: {
      type: Array,
      default() {
        return ['icon-favorfill', 'icon-favorfill', 'icon-favorfill']
      }
    },
    voidIconClass: {
      type: String,
      default: 'icon-favor'
    },
    disabledVoidIconClass: {
      type: String,
      default: 'icon-favorfill'
    },
    disabled: {
      type: Boolean,
      default: false
    },
    allowHalf: {
      type: Boolean,
      default: false
    },
    showText: {
      type: Boolean,
      default: false
    },
    showScore: {
      type: Boolean,
      default: false
    },
    textColor: {
      type: String,
      default: 'rgba(0,0,0,.65)'
    },
    texts: {
      type: Array,
      default() {
        return ['极差', '失望', '一般', '满意', '惊喜']
      }
    },
    scoreTemplate: {
      type: String,
      default: '{value}'
    }
  },
  computed: {
    text() {
      let result = ''
      if (this.showScore) {
        result = this.scoreTemplate.replace(/\{\s*value\s*\}/, this.rateDisabled
            ? this.value
            : this.currentValue)
      } else if (this.showText) {
        result = this.texts[Math.ceil(this.currentValue) - 1]
      }
      return result
    },
    decimalStyle() {
      let width = ''
      if (this.rateDisabled) {
        width = `${this.valueDecimal < 50 ? 0 : 50}%`
      }
      if (this.allowHalf) {
        width = '50%'
      }
      return {
        color: this.activeColor,
        width
      }
    },
    valueDecimal() {
      return this.value * 100 - Math.floor(this.value) * 100
    },
    decimalIconClass() {
      return this.getValueFromMap(this.value, this.classMap)
    },
    voidClass() {
      return this.rateDisabled ? this.classMap.disabledVoidClass : this.classMap.voidClass
    },
    activeClass() {
      return this.getValueFromMap(this.currentValue, this.classMap)
    },
    colorMap() {
      return {
        lowColor: this.colors[0],
        mediumColor: this.colors[1],
        highColor: this.colors[2],
        voidColor: this.voidColor,
        disabledVoidColor: this.disabledVoidColor
      }
    },
    activeColor() {
      return this.getValueFromMap(this.currentValue, this.colorMap)
    },
    classes() {
      let result = []
      let i = 0
      let threshold = this.currentValue
      if (this.allowHalf && this.currentValue !== Math.floor(this.currentValue)) {
        threshold--
      }
      for (; i < threshold; i++) {
        result.push(this.activeClass)
      }
      for (; i < this.max; i++) {
        result.push(this.voidClass)
      }
      return result
    },
    classMap() {
      return {
        lowClass: this.iconClasses[0],
        mediumClass: this.iconClasses[1],
        highClass: this.iconClasses[2],
        voidClass: this.voidIconClass,
        disabledVoidClass: this.disabledVoidIconClass
      }
    },
    rateDisabled() {
      return this.disabled
    }
  },
  watch: {
    value(val) {
      this.currentValue = val
      this.pointerAtLeftHalf = this.value !== Math.floor(this.value)
    }
  },
  methods: {
    getValueFromMap(value, map) {
      let result = ''
      if (value <= this.lowThreshold) {
        result = map.lowColor || map.lowClass
      } else if (value >= this.highThreshold) {
        result = map.highColor || map.highClass
      } else {
        result = map.mediumColor || map.mediumClass
      }
      return result
    },
    showDecimalIcon(item) {
      let showWhenDisabled = this.rateDisabled && this.valueDecimal > 0 && item - 1 < this.value && item > this.value
      /* istanbul ignore next */
      let showWhenAllowHalf = this.allowHalf &&
          this.pointerAtLeftHalf &&
          item - 0.5 <= this.currentValue &&
          item > this.currentValue
      return showWhenDisabled || showWhenAllowHalf
    },
    getIconStyle(item) {
      const voidColor = this.rateDisabled ? this.colorMap.disabledVoidColor : this.colorMap.voidColor
      return {
        color: item <= this.currentValue ? this.activeColor : voidColor
      }
    },
    selectValue(value) {
      if (this.rateDisabled) {
        return
      }
      if (this.allowHalf && this.pointerAtLeftHalf) {
        this.$emit('input', this.currentValue)
        this.$emit('change', this.currentValue)
      } else {
        this.$emit('input', value)
        this.$emit('change', value)
      }
    },
    handleKey(e) {
      if (this.rateDisabled) {
        return
      }
      let currentValue = this.currentValue
      const keyCode = e.keyCode
      if (keyCode === 38 || keyCode === 39) { // left / down
        if (this.allowHalf) {
          currentValue += 0.5
        } else {
          currentValue += 1
        }
        e.stopPropagation()
        e.preventDefault()
      } else if (keyCode === 37 || keyCode === 40) {
        if (this.allowHalf) {
          currentValue -= 0.5
        } else {
          currentValue -= 1
        }
        e.stopPropagation()
        e.preventDefault()
      }
      currentValue = currentValue < 0 ? 0 : currentValue
      currentValue = currentValue > this.max ? this.max : currentValue
      this.$emit('input', currentValue)
      this.$emit('change', currentValue)
    },
    setCurrentValue(value, event) {
      if (this.rateDisabled) {
        return
      }
      /* istanbul ignore if */
      if (this.allowHalf) {
        let target = event.target
        if (hasClass(target, 'bin-rate__item')) {
          target = target.querySelector('.bin-rate__icon')
        }
        if (hasClass(target, 'bin-rate__decimal')) {
          target = target.parentNode
        }
        this.pointerAtLeftHalf = event.offsetX * 2 <= target.clientWidth
        this.currentValue = this.pointerAtLeftHalf ? value - 0.5 : value
      } else {
        this.currentValue = value
      }
      this.hoverIndex = value
    },
    resetCurrentValue() {
      if (this.rateDisabled) {
        return
      }
      if (this.allowHalf) {
        this.pointerAtLeftHalf = this.value !== Math.floor(this.value)
      }
      this.currentValue = this.value
      this.hoverIndex = -1
    }
  },
  created() {
    if (!this.value) {
      this.$emit('input', 0)
    }
  }
}
</script>
